package mysql

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/lionsoul2014/ip2region/binding/golang/ip2region"
	"io/ioutil"
	"longmen/server/config/global"
	global2 "longmen/server/config/global"
	"longmen/server/pkg/common/api_models"
	"longmen/server/pkg/db/mysql/models/models_main"
	"net/http"
	"reflect"
	"strings"

	"gorm.io/gorm"
)

type Total struct {
	S int64
}
type TotalFloat struct {
	S float64
}

func Condition(table *gorm.DB, param map[string]interface{}, groupBy, order string, page, size int) *gorm.DB {
	if len(param) > 0 {
		for k := range param {
			if strings.ToLower(k) == "or" {
				if c, ok := param[k].([]interface{}); ok {
					if len(c) >= 2 {
						table.Where(fmt.Sprintf("(%v)", c[0]), c[1:]...)
					}
				}
				continue
			}
			if c, ok := param[k].(map[string]interface{}); ok {
				for k2, v2 := range c {
					if strings.ToLower(k2) == "between" {
						v3, ok := v2.([]interface{})
						if ok && len(v3) >= 2 {
							table.Where(fmt.Sprintf("%s %s ? and ?", k, k2), v3[0], v3[1])
						}
						continue
					}
					table.Where(fmt.Sprintf("%s %s ?", k, k2), v2)
				}
				continue
			}
			table.Where(fmt.Sprintf("%s = ?", k), param[k])
		}
	}
	if len(groupBy) > 0 {
		table.Group(groupBy)
	}
	if len(order) > 0 {
		table.Order(order)
	}
	if page > 0 && size > 0 {
		table.Limit(size).Offset((page - 1) * size)
	}
	return table
}

func GetRTable(table interface{}) *gorm.DB {
	return global2.GetR().Model(table)
}

func GetXTable(table interface{}) *gorm.DB {
	return global2.GetX().Model(table)
}

func GetSportsTable(table interface{}) *gorm.DB {
	return global2.Sport().Model(table)
}
func GetGuessTable(table interface{}) *gorm.DB {
	return global2.Guess().Model(table)
}

func InsertAnyByTx(session *gorm.DB, o interface{}, comment string) (int, error) {
	err := session.Create(o).Error
	if err != nil {
		session.Rollback()
		return 0, err
	}
	if o == nil {
		return 0, errors.New("主库增加记录错误")
	}

	vLog := reflect.ValueOf(o).Elem()
	id := vLog.FieldByName("Id")

	if id.Kind() == 0 {
		return 0, errors.New("主库增加记录错误")
	}
	return int(id.Int()), nil
}

/*
传入的o必须是一个指针类型 &somthing{}
*/
func InsertXAny(o interface{}, comment string) (int, error) {
	session := global2.GetX().Begin()
	id, err := InsertAnyByTx(session, o, comment)
	if err != nil {
		return 0, err
	}
	return id, session.Commit().Error //在这里提交
}

/*
*** 传入参数和种子，查x表的任何

	o:= &models.User{} //种子必须是指针类型否则会报错
	GetXAnyParam(map[string]interface{}{"id": uid}, o, "methodName")
*/
func GetXAnyParam(o interface{}, param map[string]interface{}, bLast bool, comment string) error {
	var err error
	t := GetRTable(o)
	if bLast {
		err = Condition(t, param, "", "", 0, 0).
			Order("id desc").Limit(1).
			Find(o).Error
	} else {
		err = Condition(t, param, "", "", 0, 0).
			Find(o).Error
	}
	if errors.Is(err, gorm.ErrRecordNotFound) {
		err = nil
	}
	if err != nil {
		return err
	}
	return nil
}

// 这个方法可以去主库里修改任何表的字段，根据oModel这个种子去找到相应的表
// 当session==nil，原地提交
// oModel里id不得为0
func UpdateXAnyParam(session *gorm.DB, columns []string, oModel interface{}, comment string) error {
	bCommitHere := false
	vLog := reflect.ValueOf(oModel).Elem()
	oId := vLog.FieldByName("Id")
	if oId.Kind() == 0 {
		return errors.New("6UpdateXAnyParam9Invalid Id")
	}
	id := int(oId.Int())
	if session == nil { //如果传入为空，就在这里提交
		bCommitHere = true
		session = global2.GetX().Begin()
	}
	err := session.Where("id = ?", id).Select(columns).Updates(oModel).Error
	if err != nil {
		session.Rollback()
		return err
	}
	if bCommitHere {
		err = session.Commit().Error
	}
	if err != nil {
		return err
	}
	return nil
}

// seed:传入要查的struct类型的指针
// 这是查表的，就用getRTable的方法
func GetXAnyListParam(seed interface{}, oList interface{}, param map[string]interface{}, comment string,
	order string, page, size int) error {
	err := Condition(GetRTable(seed), param, "", order, page, size).
		Find(oList).
		Error
	if err != nil {
		return err
	}
	return nil
}

/*
seed:传入要查的struct类型的指针
sDistinct:要去重的列
*/
func CountXAnyParam(seed interface{}, n *int64, param map[string]interface{}, comment,
	order, sDistinct string, page, size int) error {
	err := Condition(GetRTable(seed), param, "", order, page, size).Distinct(sDistinct).
		Count(n).
		Error
	if err != nil {
		return err
	}
	return nil
}

func GetUserParam(param map[string]interface{}) (*models_main.User, error) {
	o := &models_main.User{}
	table := o.TableName()
	t := global2.GetX().Table(table)
	Condition(t, param, "", "", 0, 0)
	err := t.Order("id desc").Limit(1).Find(o).Error
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		err = nil
	}
	return o, err
}

func GetIpToArea(ip string) (*api_models.GetIpSearchResp, error) {
	ipSearchResp := &api_models.GetIpSearchResp{}
	if global.Config.GetInt("area_check") != 1 {
		return ipSearchResp, nil
	}
	region, _ := ip2region.New("ip2region.db")
	if region != nil {
		ipInfo, err := region.MemorySearch(ip)
		defer region.Close()
		if err == nil {
			ipSearchResp.CityId = ipInfo.CityId
			ipSearchResp.Country = ipInfo.Country
			ipSearchResp.Region = ipInfo.Region
			ipSearchResp.Province = ipInfo.Province
			ipSearchResp.City = ipInfo.City
			ipSearchResp.ISP = ipInfo.ISP
		}
	} else {
		url := global.Config.GetString("ip.url")
		if len(url) == 0 {
			url = "http://ip-api.com/json/%s?lang=zh-CN"
		}
		url = fmt.Sprintf(url, ip)
		resp, err := http.Get(url)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		if err != nil {
			return nil, err
		}
		s1, _ := ioutil.ReadAll(resp.Body)
		err = json.Unmarshal(s1, ipSearchResp)
		if err != nil {
			return nil, err
		}
		return ipSearchResp, nil
	}
	return ipSearchResp, nil
}

func GetConfigTagByIds2(tagIDs []int64) ([]*models_main.ConfigTag, error) {
	var (
		err     error
		dbQuery = make([]*models_main.ConfigTag, 0)
	)

	if err = global.GetX().
		Raw("SELECT *  FROM db_config_tag WHERE id IN ? ", tagIDs).
		Find(&dbQuery).Error; err != nil {
		return dbQuery, err
	}

	return dbQuery, err
}

func MatchAstrictArea(getIpAstrictAreaReq *api_models.GetIpAstrictAreaReq) (*api_models.GetLiveAstrictAreaResp, error) {
	resp := &api_models.GetLiveAstrictAreaResp{}
	//area, err := GetIpToArea(getIpAstrictAreaReq.Ip)
	////查找ip地址
	//liveAstrictAreaResp := &api_models.GetLiveAstrictAreaResp{}
	//liveAstrictAreaResp.Ip = getIpAstrictAreaReq.Ip
	//if err != nil {
	//	return nil, fmt.Errorf("%s%s", util.Convert(fName), err)
	//}
	////从数据库中取区域
	//if len(area.City) > 0 || len(area.Country) > 0 || len(area.Province) > 0 {
	//	astrictArea, err := db_live.GetLiveAstrictArea(map[string]string{"city": area.City, "country": area.Country, "province": area.Province})
	//	if err != nil {
	//		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	//	}
	//	if astrictArea.Nums > 0 {
	//		liveAstrictAreaResp.Astrict = 1
	//	}
	//}
	return resp, nil
}
